﻿/* System.Compute: a Computation Framework for .NET/Mono
 * Copyright (C) 2011 Ralf "raboni" Boeckhorst
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 */

using System.Runtime.InteropServices;

namespace System.Compute.OpenCL
{
    [StructLayout(LayoutKind.Sequential)]
    public struct Half
    {
        ushort Value;

        Half(ushort val)
        {
            Value = val;
        }

        public static Half MaxValue
        {
            get
            {
                return new Half(0x7bff);
            }
        }

        public static Half MinValue
        {
            get
            {
                return new Half(0xfbff);
            }
        }

        public static Half PositiveInfinity
        {
            get
            {
                return new Half(0x7c00);
            }
        }

        public static Half NegativeInfinity
        {
            get
            {
                return new Half(0xfc00);
            }
        }

        public static Half NaN
        {
            get
            {
                return new Half(0xfe00);
            }
        }

        public static Half Epsilon
        {
            get
            {
                return new Half(0x0001);
            }
        }

        public override bool Equals(object obj)
        {
            if (obj.GetType() != typeof(Half))
                return false;
            if ((float)obj != (float)this)
                return false;
            return true;
        }

        public override int GetHashCode()
        {
            return Value;
        }

        public static bool operator ==(Half a, Half b)
        {
            return a.Equals(b);
        }

        public static bool operator !=(Half a, Half b)
        {
            return !a.Equals(b);
        }

        public static bool operator <(Half a, Half b)
        {
            return (float)a < (float)b;
        }

        public static bool operator <=(Half a, Half b)
        {
            return (float)a <= (float)b;
        }

        public static bool operator >(Half a, Half b)
        {
            return (float)a > (float)b;
        }

        public static bool operator >=(Half a, Half b)
        {
            return (float)a >= (float)b;
        }

        public static Half operator +(Half a, Half b)
        {
            return (Half)((float)a + (float)b);
        }

        public static Half operator +(Half a)
        {
            return (Half)(+(float)a);
        }

        public static Half operator ++(Half a)
        {
            float tmp = (float)a;
            return (Half)(tmp++);
        }

        public static Half operator -(Half a, Half b)
        {
            return (Half)((float)a - (float)b);
        }

        public static Half operator -(Half a)
        {
            return (Half)(-(float)a);
        }

        public static Half operator --(Half a)
        {
            float tmp = (float)a;
            return (Half)(tmp--);
        }

        public static Half operator *(Half a, Half b)
        {
            return (Half)((float)a * (float)b);
        }

        public static Half operator /(Half a, Half b)
        {
            return (Half)((float)a / (float)b);
        }

        public static implicit operator Half(float obj)
        {
            int val = BitConverter.ToInt32(BitConverter.GetBytes(obj), 0);
            int sig = val >> 31;
            int exp = (val & 0x7f800000) >> 23;
            int fra = val & 0x007fffff;
            if (exp == 0 && fra == 0) // zero
                return new Half(0x0000);
            if (exp == 0 && fra != 0) // subnormal -> -inf
                return NegativeInfinity;
            if (exp == 0xff && fra == 0) // inf
                return new Half((ushort)(sig << 15 | 0x7c00));
            if (exp == 0xff && fra != 0) // nan
                return NaN;
            // normal
            exp -= 127;
            if (exp > 15) // +inf
                return PositiveInfinity;
            if (exp < -14) // -inf
                return NegativeInfinity;
            exp += 15;
            return new Half((ushort)(sig << 15 | exp << 10 | fra >> 13));
        }

        public static implicit operator float(Half obj)
        {
            int sig = obj.Value >> 15;
            int exp = (obj.Value & 0x7c00) >> 10;
            int fra = obj.Value & 0x03ff;
            if (exp == 0 && fra == 0) // zero
                return 0f;
            if (exp == 0 && fra != 0) // subnormal -> convert
                return fra / 16777216f;
            if (sig == 0 && exp == 0x1f && fra == 0) // +inf
                return float.PositiveInfinity;
            if (sig == 1 && exp == 0x1f && fra == 0) // -inf
                return float.NegativeInfinity;
            if (exp == 0x1f && fra != 0) // nan
                return float.NaN;
            //normal
            exp = exp - 15 + 127;
            return BitConverter.ToSingle(BitConverter.GetBytes(sig << 31 | exp << 23 | fra << 13), 0);
        }
        
        public override string ToString()
        {
            return ((float)this).ToString();
        }
    }
}
